//---------------------------------------------------------------------
// Authors: Oisin Grehan
//
// Description: Archive Expansion
//
// Creation Date: Jan 7, 2007
//---------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.IO;
using System.Management.Automation;
using System.Security;

using Microsoft.PowerShell.Commands;

using Pscx.Commands.IO.Compression.ArchiveExtracter;
using Pscx.Commands.IO.Compression.ArchiveReader;
using Pscx.IO;

namespace Pscx.Commands.IO.Compression {

    /// <summary>
    /// 
    /// </summary>
    [Cmdlet(PscxVerbs.Expand, PscxNouns.Archive,
        DefaultParameterSetName = ParameterSetPath,
        SupportsShouldProcess = true)]
    [ProviderConstraint(typeof(FileSystemProvider))]
	public class ExpandArchiveCommand : PscxInputObjectPathCommandBase
	{
        public ExpandArchiveCommand()
        {
            this.Format = ArchiveFormat.Unknown;
        }

        [Parameter]
        [ValidateNotNull]
        [ValidateRange(0u, uint.MaxValue)]
        public uint[] Index
        {
            get;
            set;
        }

        [Parameter]
        [ValidateNotNullOrEmpty]
        [AcceptsWildcards(true)]
        public string[] EntryPath
        {
            get;
            set;
        }

        [
            Parameter(
                ParameterSetName = ParameterSetObject,
                Position = 0,
                Mandatory = false),
            Parameter(
                ParameterSetName = ParameterSetPath,
                Position = 1,
                Mandatory = false
                ),
            Parameter(
                ParameterSetName = ParameterSetLiteralPath,
                Position = 1,
                Mandatory = false
                ),
            Alias("To"),
            ValidateNotNullOrEmpty,
            PscxPath(
                IsLiteral = true,
                ShouldExist = true,
                PathType = PscxPathType.Container)
        ]
        public PscxPathInfo OutputPath
        {
            get; set;
        }

        [Parameter]
        public SecureString Password
        {
            get; set;
        }

	    [Parameter]
	    public SwitchParameter FlattenPaths
	    {
	        get; set;
	    }

        [Parameter]
        public SwitchParameter IgnoreCase
        {
            get; set;
        }

        [Parameter]
        public SwitchParameter PassThru
        {
            get; set;
        }

        [Parameter]
        public ArchiveFormat Format
        {
            get;
            set;
        }

        [Parameter]
        public SwitchParameter ShowProgress
        {
            get;
            set;
        }

		protected override void BeginProcessing()
		{
			base.BeginProcessing();
            
            RegisterInputType<ArchiveEntry>(ProcessArchive);
            RegisterInputType<FileInfo>(ProcessArchive);
		    
            // if OutputPath is not provided, use current filesystem path.
            if (OutputPath == null)
            {
                OutputPath = PscxPathInfo.FromPathInfo(
                    this.CurrentProviderLocation(FileSystemProvider.ProviderName));

                // todo: localize
                WriteVerbose("Using FileSystemProvider current location for OutputPath: " + OutputPath);
            }
		}

        protected override void ProcessPath(PscxPathInfo pscxPath)
        {
            var archive = new FileInfo(pscxPath.ProviderPath);
            ProcessArchive(archive);
        }

        private void ProcessArchive(FileInfo fileInfo)
        {
            WriteDebug("ProcessArchive: " + fileInfo);
            
            EnsureArchiveFormat(fileInfo);

            using (var extractor = new SevenZipExtractor(this, fileInfo, this.PassThru, Format))
            {
                extractor.OutputDirectory = OutputPath.ProviderPath;
                extractor.ShowScanProgress = this.ShowProgress;

                if ((Index == null) && (EntryPath == null))
                {
                    WriteVerbose("Expanding all.");
                    if (ShouldProcess("Archive", "Expand All"))
                    {
                        extractor.Extract();
                    }
                }
                else
                {
                    WriteVerbose(String.Format("Expanding partial set of {0} entry(ies).", this.Index.Length));

                    if (ShouldProcess("Archive", "Expand Partial"))
                    {
                        var list = new List<uint>(this.Index);
                        extractor.Extract(entry => list.Contains(entry.Index));
                    }
                }
            }
        }

        private void EnsureArchiveFormat(FileInfo fileInfo)
        {
            if (this.Format != ArchiveFormat.Unknown)
            {
                // user-specified
                return;
            }

            ArchiveFormat format = ArchiveFormat.Unknown;
            if (SevenZipBase.TryGetFormat(this, fileInfo, ref format))
            {
                WriteVerbose("Format is " + format);
                this.Format = format;
            }
            else
            {
                // todo: localize
                // unknown file extension / format - terminating
                ErrorHandler.HandleError(true,
                     new ErrorRecord(new PSArgumentException("Unknown file extension/format."),
                        "UnknownArchiveFormat", ErrorCategory.ReadError, fileInfo));
            }            
        }

        private void ProcessArchive(ArchiveEntry entry)
        {
            WriteDebug(String.Format("ProcessArchive: {0}#{1}", entry.ArchivePath, entry.Path));

            if ((Index != null) || (EntryPath != null))
            {
                // todo: localize
                WriteWarning("Ignoring Index and/or EntryPath arguments.");
            }

            using (var extractor = new SevenZipExtractor(this,
                new FileInfo(entry.ArchivePath), this.PassThru, entry.Format))
            {
                if (ShouldProcess(entry.Path, "Expand"))
                {
                    extractor.OutputDirectory = OutputPath.ProviderPath;
                    extractor.IgnoreCase = IgnoreCase;
                    extractor.ShowExtractProgress = ShowProgress;
                    
                    // do it!
                    extractor.Extract(entry);
                }
            }
        }
	}
}
